Skip to content

fix(core): a film moves on past a video the browser never starts loading - #7368

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/runtime-skip-unstarted-video
Nov 7, 2026
Merged

miguel-heygen merged 1 commit into
mainfrom
fix/runtime-skip-unstarted-video

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

A film with a video the browser never starts loading now moves on after the usual 5 s wait, instead of freezing. The runtime skips that video and names it in a warning: skipping "…/clip.mp4": the browser never started loading it; the rest plays.

Why

Safari sometimes leaves a video at networkState LOADING and readyState HAVE_NOTHING without ever sending its request. #7347 traced this in the Safari perf runs: the server receives no request, and the page has no resource-timing entry. The runtime holds playback while a due video has no data. The skip from #7355 covers a video that stopped after its header (HAVE_METADATA and NETWORK_IDLE), but not this state, so a viewer's film held at that frame indefinitely. A one-time load() does not recover it (#7347).

Related work

How

  • media.ts: stallSinceFirstAsk returns why a video is stalled 5 s after it was first asked to play. It is either "after its header" (fix(core): skip a video the browser can't decode instead of freezing the film #7355's case) or "never started": HAVE_NOTHING and NETWORK_LOADING with no progress event since it was first asked to play.
  • The progress condition keeps a slow fetch waiting. Bytes are arriving there, so the browser did start it.
  • isUnplayable uses that reason in its warning. Playback, the playhead's media hold and the audio clock already skip anything isUnplayable reports, so the film moves on.
  • An emptied event (a new source) clears both the wait and the bytes mark, as it already cleared the wait.
  • Preview and player playback only. Renders seek frame by frame and do not use this path.

Test plan

  • Unit tests added in media.test.ts: a clip at HAVE_NOTHING and NETWORK_LOADING is kept at 4999 ms, then skipped and named at 5000 ms. The same clip with a progress event is still kept at 60 s.
  • Film-level test added in init.test.ts: a muted video in that state, played for 7 s of frames, moves the playhead past 1 s and logs the warning.
  • Fails without the fix: with the "never started" case removed, the film test's playhead stays at 0.017 s after 7 s and the unit skip test fails. Ignoring progress fails the slow-fetch test.
  • media.test.ts (190 tests) and the new film test pass 3 runs in a row. The whole runtime suite passes (2864 tests). Core typecheck, test:hyperframe-runtime-ci, oxlint, oxfmt and the comment checks are clean.

Size

One state added to the existing skip check, plus three tests.

@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 103 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

Unstable (1)

  • seqplainkeys-none-pct-r0-nested-z100: tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo false, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve at 222fd344. No blockers.

Verified

  • Fails without the fix: with media.ts from origin/main, two tests fail: init.test.ts › "a video the browser never starts loading › moves the film on past it after the wait and names it", and media.test.ts › "skips and names a clip the browser never started loading 5 s after it was asked to play". Both pass at this head, and so does the slow-fetch (progress) test.
  • All of media.test.ts passes (190 tests).
  • A skipped clip can still recover. isUnplayable is checked again on every tick, so a late first byte fires progress and the clip plays again. A NotAllowedError refusal still clears the wait, and emptied clears both marks.
  • The runtime does not use MSE or HLS, so a blob: source that never fires progress cannot be wrongly skipped.

Non-blocking note: with a time to first byte over 5 s, the film now moves on without that video, and the video joins late. Before, the film held. This matches the tradeoff #7355 already made, but the Player Safari perf run on this head is still in progress, so it is worth reading before merge.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit a595755 Nov 7, 2026
98 checks passed
@miguel-heygen
miguel-heygen deleted the fix/runtime-skip-unstarted-video branch November 7, 2026 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant